package com.forcedotcom
{
	import com.forcedotcom.components.PostList;
	import com.forcedotcom.crypto.ChatterEncryption;
	import com.forcedotcom.dialogs.CommentAlertWindow;
	import com.forcedotcom.dialogs.PostAlertWindow;
	import com.forcedotcom.events.ChattergatorEvent;
	import com.forcedotcom.model.CommentItemData;
	import com.forcedotcom.model.PostItemData;
	import com.forcedotcom.model.PostListData;
	import com.salesforce.AsyncResponder;
	import com.salesforce.Connection;
	import com.salesforce.events.DebugEvent;
	import com.salesforce.events.SendEvent;
	import com.salesforce.objects.LoginRequest;
	import com.salesforce.objects.SObject;
	import com.salesforce.results.Fault;
	import com.salesforce.results.LoginResult;
	import com.salesforce.results.QueryResult;
	
	import flash.display.Bitmap;
	import flash.display.Loader;
	import flash.display.NativeWindowSystemChrome;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.TimerEvent;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.utils.Timer;
	
	import mx.collections.ArrayCollection;
	import mx.collections.Sort;
	import mx.collections.SortField;
	import mx.controls.Image;
	import mx.controls.List;
	import mx.core.FlexGlobals;
	import mx.core.IUID;
	import mx.core.mx_internal;
	import mx.managers.PopUpManager;
	import mx.rpc.AsyncToken;
	import mx.rpc.events.ResultEvent;
	import mx.utils.ObjectUtil;
	import mx.utils.URLUtil;
	
	import org.osmf.events.TimeEvent;

	[Bindable]
	public class AccountConnection extends EventDispatcher
	{
		protected var _password:String;
		protected var _token:String;
		protected var instance:AccountConnection;
		protected var loginRequest:LoginRequest = new LoginRequest();
		protected var _loginError:Boolean = false;
		
		public var connection:Connection;
		public var serverUrl:String;
		public var color:uint;
		public var headerTextColor:uint;
		public var refreshInterval:uint;
		public var postListData:ArrayCollection = new ArrayCollection();
		public var timer:Timer;
		public var loginResult:LoginResult;
		public var postList:PostList;
		public var inTimer:Boolean = false;
		public var feed:ArrayCollection = new ArrayCollection();
		public var mylist:List;
		public var profileImageSource:Object;
		public var selectedList:Number = 0;
		public var connectionName:String;
		public var photoCache:Object = {};
		public var errorText:String = "";
		
		public function get loginError():Boolean {
			return _loginError;
		}
		public function get userName():String {
			return loginRequest.username;
		}
		public function set userName(un:String):void {
			loginRequest.username = un;
		}
		public function get password():String {
			return _password;
		}
		public function set password(pw:String):void {
			_password = pw;
			loginRequest.password = _password + _token;
		}
		public function get token():String {
			return _token;	
		}
		public function set token(tk:String):void {
			_token = tk;
			loginRequest.password = _password + _token;
		}

		private function get baseUserProfileQuery():String {
			return "Select SystemModStamp, Id, CreatedDate, CreatedBy.Name, CreatedById, " + 
				"Parent.Name, ParentId, " + 
				"FeedPost.Type, FeedPost.Title, FeedPost.Body, " +
				"(SELECT Id, CommentBody, CreatedDate, CreatedById,	CreatedBy.Name " +
				"FROM FeedComments ORDER BY CreatedDate ASC LIMIT 30) ";
		}
		
		private function get userProfileFromClause():String {
			return "From UserProfileFeed ";
		}
		
		private function get orderByClause():String{
			return "ORDER BY CreatedDate DESC, Id DESC LIMIT 20";
		}
		
		private function getUserProfileNewsFeedQuery():PostListData {
			return new PostListData(
				baseUserProfileQuery, 
				userProfileFromClause, 
				withClause, 
				"", 
				orderByClause, 
				"Home Feed"); // buildUserProfileQuery(""), "Home Feed");
		}
		
		public function get withClause():String {
			return "WITH USERID = '" + loginResult.userId + "' ";
		}
		public function buildUserProfileQuery(whereClause:String):String {
			return baseUserProfileQuery +
				userProfileFromClause  + 
				withClause +
				whereClause + 
				orderByClause;
		}
		private function getNewsFeedQuery():PostListData {
			return new PostListData(
				baseUserProfileQuery,
				"From NewsFeed ",
				"",
				"",
				orderByClause,
				"News Feed");
			/*return new PostListData("Select SystemModStamp, Id, CreatedDate, CreatedBy.Name, CreatedById, " + 
				"Parent.Name, ParentId, " + 
				"FeedPost.Type, FeedPost.Title, FeedPost.Body, " +
				"(SELECT Id, CommentBody, CreatedDate, CreatedById,	CreatedBy.Name " +
				"FROM FeedComments ORDER BY CreatedDate ASC LIMIT 30) " +
				"From NewsFeed " +
				"ORDER BY CreatedDate DESC, Id DESC LIMIT 20", "News Feed");*/
			return null;
		}

		public function get serverUrlBase():String {
			return URLUtil.getProtocol(loginResult.serverUrl) + "://" + URLUtil.getServerName(loginResult.serverUrl);
		}
		
		public function stopTimer():void {
			timer.stop();
		}
		
		public function updateStatus(statusUpdate:String): void {
			var update:SObject = new SObject("User");
			update["Id"] = loginResult.userId;
			update["CurrentStatus"] = statusUpdate;
			connection.update([update],
				new AsyncResponder(
					function(sr:Object):void {
						
					}, function(sr:Object):void {
						dispatchEvent(new ChattergatorEvent(ChattergatorEvent.LOGIN_FAILURE, instance));
					}));
		}

		public function wallToWall(statusUpdate:String, fromId:String, toId:String): void {
			var update:SObject = new SObject("FeedPost");
			update["ParentId"] = toId;
			update["Body"] = statusUpdate.split("]")[1];
			connection.create([update],
				new AsyncResponder(
					function(sr:Object):void {
						
					}, function(sr:Object):void {
						dispatchEvent(new ChattergatorEvent(ChattergatorEvent.LOGIN_FAILURE, instance));
					}));
		}
		
		public function AccountConnection(userName:String=null, password:String=null, token:String=null, sUrl:String=null, clr:uint=0x000000, hdrTextClr:uint=0xFFFFFF, accountLabel:String=null, interval:uint=300000)
		{
			if (userName == null) return;
			instance = this;
			connection = new Connection();
			connection.client = "ApexMobile/Stratus0.8";
			connection.serverUrl = sUrl;
			connection.addEventListener(SendEvent.SEND_REQUEST, function(event:SendEvent):void {
				dispatchEvent(new DebugEvent(DebugEvent.DEBUG_EVENT, sUrl + "\n\n" + event.soapRequest));
			});
			serverUrl = sUrl;
			refreshInterval = interval;
			this.userName = userName;
			this.password = ChatterEncryption.decrypt(password);
			this.token = ChatterEncryption.decrypt(token);
			if (accountLabel == "") {
				this.connectionName = userName;
			} else {
				this.connectionName = accountLabel;
			}
			color = clr;
			headerTextColor = hdrTextClr;
			loginRequest.callback = new AsyncResponder(
				function(lr:LoginResult):void {
					_loginError = false;
					errorText = "";
					loginResult = lr;
					makePostList();
					if (postListData.length == 0) {
						addStandardFeeds();
					}
					dispatchEvent(new ChattergatorEvent(ChattergatorEvent.LOGIN_SUCCESS, this));
					if (refreshInterval < 1) {
						refreshInterval = 300000;
					}
					timer = new Timer(refreshInterval);
					timer.addEventListener(TimerEvent.TIMER, handleTimer);
					timer.start();
				}, function(lr:Object):void {
					_loginError = true;
					errorText = (lr as Fault).faultstring;
					dispatchEvent(new ChattergatorEvent(ChattergatorEvent.LOGIN_FAILURE, lr));
					makePostList();
				}, instance);
		}
		
		private function makePostList():void {
			if (postList == null) {
				postList = new PostList();
				postList.account = instance;
			}
			postList.bgColor = color.toString();
			postList.headerTextColor = headerTextColor.toString();
			postList.listLabel = connectionName;
			postList.width = 412;
		}
		
		private function addStandardFeeds():void {
			var plDataIn:PostListData = getNewsFeedQuery();
			plDataIn.account = this;
			postListData.addItem(plDataIn);
			var plDataIn2:PostListData = getUserProfileNewsFeedQuery();
			plDataIn2.account = this;
			postListData.addItem(plDataIn2);
		}

		private function handleTimer(event:Event):void {
			for each (var plData:PostListData in postListData) {
				getQueryData(plData);
				inTimer = true;
			}	
		}
		
		public function login():void {
			_loginError = false;
			errorText = "";
			connection.login(loginRequest);
		}
		
		public function getQueryData(plDataIn:PostListData = null):void {
			var firstTime:Boolean = plDataIn == null;
			if (!postListData.contains(plDataIn) && plDataIn != null) {
				plDataIn.account = this;
				postListData.addItem(plDataIn);
				firstTime = true;
			}
			
			for each (var pld:PostListData in postListData) {
				connection.query(pld.query, new AsyncResponder(
					function(qr:QueryResult):void {
						var plData:PostListData = qr.context as PostListData;
						for each (var obj:SObject in qr.records) {
							if (obj.FeedPost != null) {
								if (obj.FeedPost.Title != null || obj.FeedPost.Body != null) {
									var pid:PostItemData = new PostItemData(obj, instance);
									if (!plData.feed.contains(pid)) {
										plData.feed.addItemAt(pid, 0);
										if (inTimer) {
											showNewPostAlertWindow(pid);
										}
									} else {
										var oldPid:PostItemData = plData.feed.getItemAt(plData.feed.getItemIndex(pid));
										var pidIndex:int;
										if (oldPid.post.FeedComments != null) {
											if (oldPid.post.FeedComments.records.length < pid.post.FeedComments.records.length) {
												showNewCommentAlertWindow(pid, plData);
												pidIndex = plData.feed.getItemIndex(pid);
												plData.feed.removeItemAt(pidIndex);
												plData.feed.addItemAt(pid, pidIndex);
											}
										} else if (pid.post.FeedComments != null) {
											showNewCommentAlertWindow(pid, plData);
											pidIndex = plData.feed.getItemIndex(pid);
											plData.feed.removeItemAt(pidIndex);
											plData.feed.addItemAt(pid, pidIndex);
										}
									}
								}
							}
						}
						var lastObj:Object = qr.records[qr.records.length - 1];
						plData.lastItem = lastObj.SystemModstamp;
						if (firstTime) {
							plData.feed.sort = new Sort();
							plData.feed.sort.fields = [new SortField("postDate", false, true)];
							plData.feed.refresh();
						}
						rebind(plData);
					}, function(lr:Fault):void {
						dispatchEvent(new ChattergatorEvent(ChattergatorEvent.LOGIN_FAILURE, lr.context));
					}, pld));
			}
		}
		private function showNewPostAlertWindow(pid:PostItemData):void {
			var a:PostAlertWindow = new PostAlertWindow(); 
			a.title = "New Post";
			a.open();
			a.data = pid;
			a.addEventListener(Event.CLOSE, function(evt:Event):void {
				PopUpManager.removePopUp(a);
			});
		}
		
		private function showNewCommentAlertWindow(pid:PostItemData, plData:PostListData):void {
			var a:CommentAlertWindow = new CommentAlertWindow(); 
			a.title = "New Comment";
			a.open();
			a.plData = plData;
			a.data = pid;
			a.addEventListener(Event.CLOSE, function(evt:Event):void {
				PopUpManager.removePopUp(a);
			});
		}

		public function setSelectedList(newIndex:Number):void {
			selectedList = newIndex;
			postList.dataProvider = (postListData.getItemAt(selectedList) as PostListData).feed;
		}
		
		private function rebind(plData:PostListData):void {
			if (postListData.getItemIndex(plData) == selectedList) {
				if (postList.scroller != null) {
					var vScroll:int = postList.verticalScrollPosition;
					var x:Number = postList.scroller.scrollRect.x;
				}
				if (postList.dataProvider == null) {
					plData.feed.plData = plData;
					postList.dataProvider = plData.feed;
				}
				if (postList.scroller != null) {
					postList.scroller.scrollRect.x = x;
				}
				dispatchEvent(new ChattergatorEvent(ChattergatorEvent.QUERY_DATA_READY, { "acctConn":instance, "plData":plData} ));
			}
		}
		
		public function getPhoto(userId:String, target:Object):void {
			if (photoCache[userId] && photoCache[userId].bitmapData) {
				target.profilePic = photoCache[userId];
			} else {
				loadPhoto(userId, target);
			}
		}

		public function loadPhoto( userId:String, target:Object ) : AsyncToken {
			
			var token : AsyncToken = new AsyncToken();
			
			var loader : Loader = new Loader();
			loader.addEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, 
				function( event : Event ) : void {
				var bitmap : Bitmap = Bitmap( loader.content );
				// replace source with loaded bitmap
				//_sourceForUserId[ userId ] = bitmap;
				target.profilePic = bitmap;
				photoCache[userId] = bitmap;
				// and notify listeners
				//token.mx_internal::applyResult( ResultEvent.createEvent( bitmap, token ));
			});
			
			connection.query( "select PhotoVersion from Photo where ParentId = '" + userId + "'", new mx.rpc.Responder( function( result : QueryResult ) : void {
				if ( !result || !result.records || result.records.length == 0 )
					return;
				
				var photoVersion : String = result.records[ 0 ].PhotoVersion;
				var sid : String = connection.getCurrentSessionid();
				var urlString : String = serverUrlBase.replace("-api", "") + "/userphoto?u=" + userId + "&v=" + photoVersion + "&s=T";
				var urlRequest : URLRequest = new URLRequest( urlString );
				var headers : Array = urlRequest.requestHeaders;
				urlRequest.manageCookies = false;
				headers.push( new URLRequestHeader( "Cookie", "sid=" + sid ));
				loader.load( urlRequest );
				
			}, function( info : com.salesforce.results.Fault ) : void {
				trace(ObjectUtil.toString(token));
			}));
			
			return token;
		}
		
		private function ioErrorHandler( event : IOErrorEvent ) : void {
			trace( "ioErrorHandler: " + event.text );
		}

		public function toXml():XML {
			var lrXML:XML = new XML('<account name="' + userName + '"  />');
			lrXML.appendChild(new XML("<username>" + userName + "</username>"));
			lrXML.appendChild(new XML('<password>' + ChatterEncryption.encrypt(password) + '</password>'));
			lrXML.appendChild(new XML('<token>' + ChatterEncryption.encrypt(token) + '</token>'));
			lrXML.appendChild(new XML('<color>' + color + '</color>'));
			lrXML.appendChild(new XML('<headerTextColor>' + headerTextColor + '</headerTextColor>'));
			lrXML.appendChild(new XML('<connectionName>' + connectionName + '</connectionName>'));
			var qXML:XML = new XML('<queries />');
			for each (var plData:PostListData in postListData) {
				qXML.appendChild(new XML('<query>' + plData.query + '</query>'));
			}
			lrXML.appendChild(qXML);
			lrXML.appendChild(new XML('<serverUrl>' + serverUrl + '</serverUrl>'));
			lrXML.appendChild(new XML('<interval>' + refreshInterval + '</interval>'));
			return lrXML;
		}
	}
}